package com.xxc.batis.service

import com.github.pagehelper.PageHelper
import com.xxc.batis.ext.toVO
import com.xxc.batis.mapper.CrudMapper
import com.xxc.batis.po.PO
import com.xxc.batis.qo.PageQO
import com.xxc.batis.vo.PageVO
import com.xxc.common.const.NOW
import org.slf4j.LoggerFactory
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.util.Assert
import tk.mybatis.mapper.entity.Condition
import tk.mybatis.mapper.entity.Example

import java.lang.reflect.ParameterizedType
import java.util.ArrayList

abstract class CrudServiceImpl<E : PO<PK>, PK,MAPPER:CrudMapper<E>> : CrudService<E, PK> {

    private val log = LoggerFactory.getLogger(CrudServiceImpl::class.java)

    @Autowired
    protected lateinit var crudMapper: MAPPER

    protected var poType: Class<E>

    init {
        val pt = this.javaClass.genericSuperclass as ParameterizedType
        poType = pt.actualTypeArguments[0] as Class<E>
    }

    override fun insert(record: E): PK {
        Assert.notNull(record, "record is not null")

        if (record.gmtCreate == null) {
            record.gmtCreate = NOW()
            record.gmtModified = NOW()
        }
        crudMapper!!.insertSelective(record)
        return record.id!!
    }

    override fun deleteByPk(pk: PK): Int {
        Assert.notNull(pk, "pk is not null")

        return crudMapper!!.deleteByPrimaryKey(pk)
    }

    override fun deleteByPks(pks: Iterable<PK>): Int {
        Assert.notNull(pks, "pks is not null")

        val pksStr = this.IterableToSpitStr(pks, ",") ?: return 0

        return crudMapper!!.deleteByIds(pksStr)
    }

    override fun updateByPk(pk: PK, record: E): Int {
        Assert.notNull(pk, "pk is not null")
        Assert.notNull(record, "record is not null")

        record.id = pk
        if (record.gmtModified == null) {
            record.gmtModified = NOW()
        }
        return crudMapper!!.updateByPrimaryKey(record)
    }

    override fun updateByPkSelective(pk: PK, record: E): Int {
        Assert.notNull(pk, "pk is not null")
        Assert.notNull(record, "record is not null")

        record.id = pk
        if (record.gmtModified == null) {
            record.gmtModified = NOW()
        }
        return crudMapper!!.updateByPrimaryKeySelective(record)
    }

    override fun saveOrUpdate(record: E): PK {
        Assert.notNull(record, "record is not null")

        if (null != record.id && null != selectByPk(record.id!!)) {
            updateByPk(record.id!!, record)
        } else {
            insert(record)
        }
        return record.id!!
    }

    override fun selectByPk(pk: PK): E? {
        Assert.notNull(pk, "pk is not null")

        return crudMapper!!.selectByPrimaryKey(pk)
    }

    override fun selectByPks(pks: Iterable<PK>): List<E> {
        Assert.notNull(pks, "pks is not null")

        val pksStr = this.IterableToSpitStr(pks, ",") ?: return ArrayList()

        return crudMapper!!.selectByIds(pksStr)
    }

    private fun IterableToSpitStr(pks: Iterable<PK>, separator: String): String? {
        val s = StringBuilder()
        pks.forEach { pk -> s.append(pk).append(separator) }

        if (s.isNullOrBlank()) {
            return null
        } else {
            s.deleteCharAt(s.length - 1)
        }

        return s.toString()
    }

    override fun selectAll(): List<E> {
        return crudMapper!!.selectAll()
    }

    override fun selectPage(pageQO: PageQO<*>): PageVO<E> {
        Assert.notNull(pageQO, "pageQO is not null")

        val page = PageHelper.startPage<E>(pageQO.curPage, pageQO.pageSize, pageQO.orderBy)
        try {
            val condition = pageQO.condition
            if (condition == null) {
                crudMapper!!.selectAll()
            } else if (condition is Condition) {
                crudMapper!!.selectByCondition(condition)
            } else if (condition is Example) {
                crudMapper!!.selectByExample(condition)
            } else if (poType.isInstance(condition)) {
                crudMapper!!.select(condition as E?)
            } else {
                try {
                    val e = poType.newInstance()
                    BeanUtils.copyProperties(condition, e)
                    crudMapper!!.select(e)
                } catch (e: InstantiationException) {
                    log.error("selectPage occurs error, caused by: ", e)
                    throw RuntimeException("poType.newInstance occurs InstantiationException or IllegalAccessException", e)
                } catch (e: IllegalAccessException) {
                    log.error("selectPage occurs error, caused by: ", e)
                    throw RuntimeException("poType.newInstance occurs InstantiationException or IllegalAccessException", e)
                }

            }
        } finally {
            page.close()
        }

        return page.toVO{ it }
    }

}